/*
 * Copyright 2006-2008 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.martinimix.dao;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

import net.martinimix.beans.bind.BindingService;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.bluemartini.dna.BMException;
import com.bluemartini.dna.BusinessObject;
import com.bluemartini.dna.BusinessObjectArray;
import com.bluemartini.dna.DNAList;
import com.bluemartini.server.BMClient;

/**
 * Provides a business action template to simplify business action execution.
 * 
 * @author Scott Rossillo
 *
 */
public class BusinessActionTemplate {
	
	private final static Log log = LogFactory.getLog(BusinessActionTemplate.class);

	/**
	 * The binding service for the business action template.
	 */
	protected BindingService bindingService;
	protected BusinessActionToolkit businessActionToolkit;
	
	/**
	 * Constructs a new business action template with the given binding service.
	 * 
	 * @param bindingService the <code>BindingService</code> for this template
	 */
	public BusinessActionTemplate(BindingService bindingService) {
		
		if(bindingService == null) {
			throw new IllegalArgumentException("[BindingService] cannot be null!");
		}
		
		this.bindingService = bindingService;
		this.businessActionToolkit = new BusinessActionToolkit(bindingService);
	}
	
	/**
	 * Creates a new business action template without initializing
	 * this template's <code>BindingService</code>
	 */
	protected BusinessActionTemplate() { }
	
	/**
	 * Executes the named business action with the given input.
	 * 
	 * @param action the name of the business action to execute
	 * 
	 * @param input a <code>Map</code> containing inputs for the
	 * <code>action</code>
	 * 
	 * @return a <code>Map</code> containing the output of the
	 * requested <code>action</code>
	 *  
	 */
	public Map execute(String action, Map input, BusinessActionResponseMapper mapper) {
		
		return execute(action, input, mapper, true);
	}
	
	/**
	 * Executes the named business action
	 * 
	 * @param action the name of the business action to execute
	 * 
	 * @param input a <code>Map</code> containing inputs for the
	 * <code>action</code>
	 * 
	 * @param wrapFormData <code>true</code> to wrap the action's input in a <code>DNAList</code>
	 * and present it as "form data"; <code>false</code> otherwise
	 * 
	 * @return a <code>Map</code> containing the output of the
	 * requested <code>action</code>
	 *  
	 */
	public Map execute(String action, Map input, BusinessActionResponseMapper mapper, boolean wrapFormData) {
		
		return createResponse(executeInternal(action, createBusinessActionInput(input, wrapFormData)), mapper);
	}
	
	/**
	 * Executes the named action with the given dna list as the input
	 * form data.
	 * 
	 * @param action the name of the business action to execute
	 * 
	 * @param input a <code>DNAList</code> containing business objects
	 * given as input the the executed <code>action</code>
	 * 
	 * @return a <code>DNAList</code> containing the response from
	 * the executed <code>action</code>
	 */
	protected DNAList executeInternal(String action, DNAList input) {

		DNAList response = null;
		
		if(log.isDebugEnabled()) {
			log.debug("Executing [" + action + "] with input [" + input + "]");
		}
				
		try {
			response = BMClient.executeBusinessAction(action, input);
			if(log.isDebugEnabled()) {
				log.debug("Executed [" + action + "], got response [" + response + "]");
			}
		} catch (BMException e) {
			throw new BusinessActionInvocationException("Error invocating business action [" + action + "]", e);
		}
		
		return response;
	}
	
	/**
	 * Returns a DNA list containing the business object equivalents of the given beans
	 * optionally wrapped in another dna list as "form data".
	 * 
	 * @param input a <code>Map</code> containing beans to be converted to 
	 * <code>BusinessObject</code>s and added the returned <code>DNAList</code>
	 * 
	 * @param wrapFormData <code>true</code> if the given <code>input</code> should be treated
	 * as "form data" and wrapped in an outer input list; <code>false</code> otherwise
	 * 
	 * @return a <code>DNAList</code> containing the given <code>input</code> beans as
	 * business objects
	 */
	protected DNAList createBusinessActionInput(Map input, boolean wrapFormData) {
		
		return businessActionToolkit.createBusinessActionInput(input, wrapFormData);
	}
	
	/**
	 * Creates a business object array from the contents of the given collection.
	 * 
	 * @param input the <code>Collection</code> containing beans to be converted to
	 * business objects returned in a <code>BusinessObjectArray</code>
	 * 
	 * @param type the business object type 
	 * 
	 * @return a <code>BusinessObjectArray</code> containing <code>BusinessObject</code>s
	 * of the given <code>type</code> converted from beans in the given <code>input</code>
	 */
	protected BusinessObjectArray createArray(Collection input, String type) {
		
		BusinessObjectArray array = new BusinessObjectArray(input.size());
		BusinessObject businessObject;
		Object o;
		
		for(Iterator it = input.iterator(); it.hasNext(); ) {
			o = it.next();
			businessObject = bindingService.bindBusinessObject(o, type);
			array.addElement(businessObject);
		}
		
		return array;
	}
	
	private Map createResponse(DNAList response, BusinessActionResponseMapper mapper) {
		
		return mapper.map(response);
	}
}
